/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010

    This file is part of Maxsi Engine.

    Maxsi Engine is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	*/ #include "MaxsiEngineContributors.h" /*

	If you modify this file, please enter your name below and provide contact
	information in MaxsiEngineContributors.h. For more information please see
	MaxsiEngineContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]

	MaxsiEngine.dll
	A linkable version of Maxsi Engine

	MaxsiInstallScript.cpp
	Functions that execute install scripts, and interfaces for declaring
	functions that can be used by insall scripts.

****************************************************************************/

#include "MaxsiEngine.h"

namespace MaxsiEngine
{
	static	List	MISPermissions	=	0;

	char* MaxsiInstallScriptInput::operator[]( size_t val )
	{
		return (char*)GetListEntry(Var,(unsigned int)val);
	}

	void MaxsiInstallScriptInput::operator =(MaxsiEngine::List Input)
	{
		Var		=	Input;
		Size	=	GetListSize(Input);
	}

	List MaxsiInstallScriptInput::operator =(MaxsiEngine::MaxsiInstallScriptInput Input)
	{
		return Var;
	}

	MaxsiInstallScriptInput::MaxsiInstallScriptInput(List Input)
	{
		Var		=	Input;
		Size	=	GetListSize(Input);
	}


	MaxsiInstallScriptInput::MaxsiInstallScriptInput()
	{
		Var		=	0;
		Size	=	0;
	}

	MISFUNCH(InstallScriptableFunction);
	MISFUNC(InstallScriptableFunction,MaxsiEngine)
	{
		return 1;
	}

	int CallFunc()
	{
		MISInput In = CreateList(2);
		InstallScriptableFunction(In);
		return 1;
	
	}

	// Register a function that can be called from MIS Scripts
	bool	MISFuncRegister		(char* Name, MISFunc Pointer, MISPermission Permission)
	{
		bool	Result	=	false;

		// Set up basic defines
		MISFuncS		S;
		MISFuncP		P;
		S.Name		=	Name;
		S.Pointer	=	Pointer;

		if ( !MISPermissions )
		{
			// Do these things here, because it's faster if we don't have a MISPermissions list.
			MISPermissions	=	CreateList(1);
			P.Functions		=	CreateList(1);
			P.Name			=	Permission;

			SetListEntry(P.Functions,	0,(char*)&S,sizeof(S));
			SetListEntry(MISPermissions,0,(char*)&P,sizeof(P));

			Result			= true;
		}
		else
		{
			// First find the proper premission
			size_t	nMISPermissions		=	GetListSize(MISPermissions);
			for (size_t N = 0; N < nMISPermissions; N++)
			{
				memcpy(&P,GetListEntry(MISPermissions,(unsigned int)N),sizeof(P));
				if ( _stricmp(Permission,P.Name) == 0)
				{
					// Then see if the function already exists.
					bool	AlreadyRegistered	=	false;
					size_t	nFunctions			=	GetListSize(P.Functions);
					for (size_t I = 0; I < nFunctions; I++)
					{
						memcpy(&S,GetListEntry(P.Functions,(unsigned int)I),sizeof(P));
						if ( _stricmp(Name,S.Name) == 0)
						{
							// The function is already registered,
							// don't add it twice.
							AlreadyRegistered	=	true;
						}
					}

					if (Not(AlreadyRegistered))
					{				
						// Reset the variables
						S.Name		=	Name;
						S.Pointer	=	Pointer;
						
						SetListSize	(P.Functions,GetListSize(P.Functions)+1);
						SetListEntry(P.Functions,(unsigned int)GetListSize(P.Functions)-1,(char*)&S,sizeof(S));
						
						Result		=	true;
					}
					else
					{
						Result		=	true;
					}
				}

				// The correct permission was not found, create a new one!
				if (Result==false)
				{					
					P.Functions		=	CreateList(1);
					P.Name			=	Permission;

					// Simply fill it into the table, expand as needed, and put the first function in as well!
					SetListEntry(P.Functions,	0,(char*)&S,sizeof(S));
					SetListEntry(MISPermissions,0,(char*)&P,sizeof(P));
					SetListSize	(MISPermissions,(unsigned int)GetListSize(MISPermissions)+1);
					SetListEntry(MISPermissions,(unsigned int)GetListSize(MISPermissions)-1,(char*)&P,sizeof(P));

					Result			=	true;
				}
			}
		}
		
		return Result;
	}

	LINK MISFunc MISFuncGet(char* Name, MISPermission Permission)
	{
		MISFunc	Result	=	0;

		// Set up basic defines
		MISFuncS		S;
		MISFuncP		P;

		if ( MISPermissions )
		{
			// First find the proper premission
			size_t	nMISPermissions		=	GetListSize(MISPermissions);
			for (size_t N = 0; N < nMISPermissions; N++)
			{
				memcpy(&P,GetListEntry(MISPermissions,(unsigned int)N),sizeof(P));
				if ( _stricmp(Permission,P.Name) == 0)
				{
					// Then see if the function already exists.
					size_t	nFunctions			=	GetListSize(P.Functions);
					for (size_t I = 0; I < nFunctions; I++)
					{
						memcpy(&S,GetListEntry(P.Functions,(unsigned int)I),sizeof(P));
						if ( _stricmp(Name,S.Name) == 0)
						{
							return S.Pointer;
						}
					}
				}
			}
		}
		
		return Result;
	}

	// Parses a Application/x-MaxsiEngineInstallerScript/2.0 script
	// and executes it and returns the result.
	LINK int MISRun(char* Script, MISPermission Permission)
	{
		// Variables
		size_t		Read		=	0;
		//size_t		Len			=	strlen(Script); // Obsolete
		size_t		NextR		=	0;
		size_t		NextN		=	0;
		size_t		NextLine	=	0;
		size_t		NextLineLen	=	0;
		size_t		NextWS		=	0;
		size_t		LeadingWS	=	0;
		size_t		argc		=	0;
		size_t		ParamLen	=	0;
		char*		Line		=	Script;
		char*		Function	=	0;
		char*		Param		=	0;
		bool		Headers		=	false;
		bool		HeadersDone	=	false;
		//bool		bTrailing	=	false; // Obsolete
		bool		bNoSupport	=	false;
		bool		LastLine	=	false;
		MISInput	In			=	0;
		List		InList		=	0;



		// Run through every line!
		while (true)
		{
			// Simply point to the location of the script
			// pointing is father than copying

			Line				=	Script+Read;

			// Find the lenght of the line, no matter how the linebreaks are encoded.
			NextR		=	FindString(Line,"\r");
			NextN		=	FindString(Line,"\n");
			if ( NextR < NextN ) { NextLine = NextR; } else { NextLine = NextN; }

			if ( NextLine == ULONG_MAX )
			{
				// Hmm.
				LastLine	= true;
				NextLine	= strlen(Line);
			}

			if ( Script[Read+NextLine] == '\r' &&
				 Script[Read+NextLine+1] == '\n'	)
			{
				NextLineLen		=	2;				
			}
			else
			{
				NextLineLen		=	1;
			}


			LeadingWS			=	SkipWhiteSpace(Line,NextLine);

			// Check if there's any content in this line, and if so, skip it
			if (LeadingWS == ULONG_MAX)
			{
				if ( Headers ) { HeadersDone = true; }
				// No content. Simply skip to next line.
				Read			+=	NextLine+NextLineLen;
				if ( LastLine )	{ break; }
				continue;
			}

			Line				=	Line+SkipWhiteSpace(Line,NextLine);

			// Find the function name.

			NextWS				=	NextWhiteSpace(Line,NextLine);

			if ( NextWS == ULONG_MAX)
			{
				// No arguments, just a function.
				Function				=	new char[NextLine+1];
				Function[NextLine]	=	0;
				memcpy(Function,Line,NextLine);
				InList					=	CreateList(0);
				argc					=	0;
			}
			else
			{
				Function				=	new char[NextWS+1];
				Function[NextWS]		=	0;
				memcpy(Function,Line,NextWS);

				// Count the number of arguments!
				argc					=	0;				
				NextWS					=	NextWS+SkipWhiteSpace(Line+NextWS+1,NextLine-NextWS);	

				while ( NextWhiteSpace(Line+NextWS,NextLine-NextWS) != ULONG_MAX )
				{
					argc++;
					if ( NextWhiteSpace(Line+NextWS+1,NextLine-NextWS) == ULONG_MAX ) { break; }
					NextWS=NextWS+NextWhiteSpace(Line+NextWS+1,NextLine-NextWS);
					if ( SkipWhiteSpace(Line+NextWS+1,NextLine-NextWS) == ULONG_MAX ) { break; }
					NextWS=NextWS+SkipWhiteSpace(Line+NextWS+1,NextLine-NextWS);
				}

				InList					=	CreateList(argc);

				// Copy the arguments into the array
				
				// Reset variables
				NextWS					=	NextWhiteSpace(Line,NextLine);		
				NextWS					=	NextWS+SkipWhiteSpace(Line+NextWS+1,NextLine-NextWS);	
				argc					=	0;

				while ( NextWhiteSpace(Line+NextWS,NextLine-NextWS) != ULONG_MAX )
				{
					argc++;
					//ParamLen			=	min(NextWhiteSpace(Line+NextWS+1,NextLine-NextWS),strlen(Line));
					if ( NextWhiteSpace(Line+NextWS+1,NextLine-NextWS) < strlen(Line) )
					{
						ParamLen		=	NextWhiteSpace(Line+NextWS+1,NextLine-NextWS);
					}
					else
					{
						ParamLen		=	strlen(Line);
					}

					Param				=	new char[ParamLen+1];
					Param[ParamLen]		=	0;
					memcpy(Param,Line+NextWS+1,ParamLen);
					char*		Variable	=	EvaluateQuote(Param);
					SetListEntry(InList,(unsigned int)argc-1,Variable,strlen(Variable)+1);
					delete[]	Variable;
					if ( NextWhiteSpace(Line+NextWS+1,NextLine-NextWS) == ULONG_MAX ) { break; }
					NextWS=NextWS+NextWhiteSpace(Line+NextWS+1,NextLine-NextWS);
					if ( SkipWhiteSpace(Line+NextWS+1,NextLine-NextWS) == ULONG_MAX ) { break; }
					NextWS=NextWS+SkipWhiteSpace(Line+NextWS+1,NextLine-NextWS);
				}
			}

			if ( strcmp(Function,"//") == 0 )
			{
				// Comment. IGNORE.
			}
			else
			{
				if ( !Headers && !HeadersDone )
				{
					Headers = true;
					if ( _stricmp(Function,"Application/x-MaxsiEngineInstallerScript/2.0") != 0 )
					{
						bNoSupport = true;
					}					
				}
				else if ( Headers && !HeadersDone )
				{
					// still boring header stuff. Skip!
					// TODO: Parse these important variables!
				}
				else
				{
					// If we're here, then we better execute something!
					MISFunc		CallMe		=	MISFuncGet(Function,Permission);

					if ( !CallMe )
					{
						// fail!
						bNoSupport = true;
					}
					else
					{
						In		=		InList;

						CallMe(In);
					}
				}
			}

			delete[] Function;
			DeleteList(InList);

			if ( bNoSupport )
			{
				return 0;
			}
			
			Read		=	Read+NextLine+NextLineLen;

			if ( LastLine )
			{
				break;
			}
		}


		return 1;
	}
}
